import { AstTreeUtils } from "arkanalyzer";
import { ts, NodePredicate } from "qvog-dsl";
import { native, ObjectLiteralExpression, rule, HomeCheckHint } from "qvog-dsl";

const SEVERITY = 2;
const DOC_PATH = '';
const DESCRIPTION = 'The array literal notation [] is preferable';

let classCount: Map<ts.SourceFile, number> = new Map();

export const NewAndCallExpression: NodePredicate<ts.Declaration> = {
    is(node: ts.Node): node is ts.Declaration {
        return ts.isNewExpression(node) ||
            ts.isCallExpression(node) ||
            ts.isArrowFunction(node) ||
            ts.isFunctionDeclaration(node);
    },
};

export default native<HomeCheckHint>()()
    .match(NewAndCallExpression)
    .when((node: ts.Declaration): boolean => {
        const visitNode = (node: ts.Node): boolean => {
            if (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node)) {// 过滤特殊语句
                const hasArrayParam = node.parameters.some(param => {
                    return ts.isIdentifier(param.name) && param.name.text === 'Array';
                });
                // 直接返回，因为这里的 Array 是参数，不是全局的 Array 构造函数
                if (hasArrayParam) {
                    return false;
                };
            }
            if (ts.isNewExpression(node) || ts.isCallExpression(node)) {
                return checkNewAndCallExpression(node);
            };
            if (ts.forEachChild(node, visitNode)) {
                return true;
            }
            return false;
        };
        return visitNode(node);
    })
    .report({
        severity: SEVERITY,
        description: DESCRIPTION,
        docPath: DOC_PATH,
    });

function checkNewAndCallExpression(node: ts.NewExpression | ts.CallExpression): boolean {
    const callee = node.expression;
    // 跳过泛型检测 new Array<Foo>() 或 Array<Foo>()
    if (
        (ts.isNewExpression(node) || ts.isCallExpression(node)) &&
        node.typeArguments && // 检测泛型参数
        ts.isIdentifier(callee) &&
        callee.text === 'Array'
    ) {
        return false;
    };
    if (
        ts.isCallExpression(node) &&
        node.questionDotToken &&
        (ts.isIdentifier(callee) || ts.isPropertyAccessExpression(callee))
    ) {
        //跳过 Array?.(x, y);
        if (node.arguments.length > 1) {
            return false;
        };
    }
    if (ts.isParenthesizedExpression(callee)) {
        const innerExpression = callee.expression;
        if (ts.isIdentifier(innerExpression) && innerExpression.text === 'Array') {
            return true;
        };
    } else if (ts.isIdentifier(callee) && callee.text === 'Array') {
        return checkGlobalArray(node);
    }
    return false;
};

function checkGlobalArray(node: ts.NewExpression | ts.CallExpression): boolean {
    const callee = node.expression;
    const isGlobalArray = !isInLocalScope(callee);
    if (isGlobalArray) {
        if (!node.arguments || node.arguments.length === 0) {
            if (ts.isArrowFunction(node) || ts.isFunctionDeclaration(node)) {
                return false;
            };
            return true;
        } else if (node.arguments.length === 1) {
            const arg = node.arguments[0];
            if ((ts.isSpreadElement(arg) && arg.getText().includes('...'))) {
                return true;
            }
        } else {
            return true;// Array(x, y, z) 或 new Array(x, y, z)，多个参数
        }
    }
    return false;
};

function isInLocalScope(node: ts.Node): boolean {
    let current = node.parent;
    while (current) {
        if (ts.isNewExpression(current)) {
            if (current.expression.getText() === 'Array' && current.arguments === undefined) {
                return false;
            };
        };
        if (ts.isVariableDeclaration(current) && current.name === node) {
            return true;
        }
        if (ts.isFunctionDeclaration(current) || ts.isModuleDeclaration(current)) {
            break;
        }
        current = current.parent;
    }
    return false;
};